home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / pop2serv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-08  |  14.6 KB  |  662 lines

  1. /* POP2 Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in pop2cli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  *    some code streaming - DB3FL.911111
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <time.h>
  12. #include <sys/stat.h>
  13. #ifdef UNIX
  14. #include <sys/types.h>
  15. #endif
  16. #if    defined(__STDC__) || defined(__TURBOC__)
  17. #include <stdarg.h>
  18. #endif
  19. #include <ctype.h>
  20. #include <setjmp.h>
  21. #include "global.h"
  22. #include "config.h"
  23. #ifdef POP2_SERVER
  24. #include "mbuf.h"
  25. #include "cmdparse.h"
  26. #include "socket.h"
  27. #include "proc.h"
  28. #include "files.h"
  29. #include <fcntl.h>
  30.  
  31. #undef LOG
  32.  
  33. #define    BITS_PER_WORD        16
  34. #define BUF_LEN        128
  35.  
  36. /* mail separator */
  37. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  38.  
  39. /* ---------------- common server data structures ---------------- */
  40.  
  41. /* POP2 server control block */
  42.  
  43. struct pop_scb {
  44.     int    socket;            /* socket number for this connection */
  45.     char    state;            /* server state */
  46. #define            LSTN        0
  47. #define            AUTH        1
  48. #define            MBOX        2
  49. #define            ITEM        3
  50. #define               NEXT        4
  51. #define            DONE        5
  52.     char    buf[BUF_LEN],        /* input line buffer */
  53.         count,            /* line buffer length */
  54.         username[64];        /* user/folder name */
  55.     FILE    *wf;            /* work folder file pointer */
  56.     int    folder_len,        /* number of msgs in current folder */
  57.         msg_num;        /* current msg number */
  58.     long    msg_len;        /* length of current msg */
  59.     int    msg_status_size;     /* size of the message status array */
  60.     long    curpos,            /* current msg's position in file */
  61.         folder_file_size,     /* length of the current folder file, in bytes */
  62.         nextpos;        /* next msg's position in file */
  63.     unsigned int folder_modified,    /*  mail folder contents modified flag */
  64.         *msg_status;        /* message status array pointer */
  65. };
  66.  
  67. #define NULLSCB        (struct pop_scb *)0
  68.  
  69. /* Response messages */
  70.  
  71. static char    count_rsp[]    = "#%d messages in this folder\n",
  72.         error_rsp[]    = "- ERROR: %s\n",
  73.         greeting_msg[] = "+ POP2 %s\n",
  74. /*        length_rsp[]   = "=%ld bytes in this message\n", */
  75.         length_rsp[]   = "=%ld characters in Message #%d\n",
  76.         msg_line[]     = "%s\n",
  77.         no_mail_rsp[]  = "+ No mail, sorry\n",
  78.         no_more_rsp[]  = "=%d No more messages in this folder\n",
  79.         signoff_msg[]  = "+ Bye, thanks for calling\n";
  80.  
  81.  
  82. static int Spop = -1; /* prototype socket for service */
  83.  
  84. /* Command string specifications */
  85. static char ackd_cmd[]     = "ACKD",
  86.         acks_cmd[]     = "ACKS",
  87. #ifdef POP_FOLDERS
  88.         fold_cmd[]     = "FOLD ",
  89. #endif
  90.         login_cmd[] = "HELO ",
  91.         nack_cmd[]     = "NACK",
  92.         quit_cmd[]     = "QUIT",
  93.         read_cmd[]     = "READ",
  94.         retr_cmd[]     = "RETR";
  95.  
  96. static void near
  97. state_error(scb,msg)
  98. struct pop_scb *scb;
  99. char *msg;
  100. {
  101.     usprintf(scb->socket,error_rsp,msg);
  102.     scb->state = DONE;
  103. }
  104.  
  105. static int near
  106. newmail(scb)
  107. struct pop_scb *scb;
  108. {
  109.     char *folder_pathname;
  110.     struct stat folder_stat;
  111.     int s;
  112.  
  113.     folder_pathname = mxallocw(strlen(Mailspool) + strlen(scb->username) + 5);
  114.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  115.     s = stat(folder_pathname,&folder_stat);
  116.     xfree(folder_pathname);
  117.  
  118.     if(s) {
  119.         state_error(scb,"Unable to get old mail folder's status");
  120.         return(FALSE);
  121.     } else {
  122.         return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
  123.     }
  124. }
  125.  
  126. static int near
  127. isdeleted(scb,msg_no)
  128. struct pop_scb *scb;
  129. int msg_no;
  130. {
  131.     unsigned int mask = 1, offset;
  132.  
  133.     msg_no--;
  134.     offset = msg_no / BITS_PER_WORD;
  135.     mask <<= msg_no % BITS_PER_WORD;
  136.     return (((scb->msg_status[offset]) & mask) ? TRUE : FALSE);
  137. }
  138.  
  139. static void near
  140. close_folder(scb)
  141. struct pop_scb *scb;
  142. {
  143.     char folder_pathname[64], line[BUF_LEN];
  144.     FILE *fd;
  145.     int deleted = FALSE, msg_no = 0;
  146.     struct stat folder_stat;
  147.  
  148.     if (scb->wf == NULL)
  149.         return;
  150.  
  151.     if (!scb->folder_modified) {
  152.         /* no need to re-write the folder if we have not modified it */
  153.  
  154.         fclose(scb->wf);
  155.         scb->wf = NULL;
  156.  
  157.         xfree((char *)scb->msg_status);
  158.         scb->msg_status = NULL;
  159.         return;
  160.     }
  161.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  162.  
  163.     if (newmail(scb)) {
  164.         /* copy new mail into the work file and save the
  165.            message count for later */
  166.         if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
  167.             return;
  168.  
  169.         fseek(scb->wf,0,SEEK_END);
  170.         fseek(fd,scb->folder_file_size,SEEK_SET);
  171.         while (!feof(fd)) {
  172.             fgets(line,BUF_LEN,fd);
  173.             fputs(line,scb->wf);
  174.         }
  175.  
  176.         fclose(fd);
  177.     }
  178.     /* now create the updated mail folder */
  179.     if ((fd = open_file(folder_pathname,"w",0,1)) == NULLFILE)
  180.         return;
  181.  
  182.     rewind(scb->wf);
  183.     while (!feof(scb->wf)){
  184.         fgets(line,BUF_LEN,scb->wf);
  185.  
  186.         if (isSOM(line)){
  187.             msg_no++;
  188.             deleted = (msg_no <= scb->folder_len) ?
  189.                 isdeleted(scb,msg_no) : FALSE;
  190.         }
  191.         if (deleted)
  192.             continue;
  193.  
  194.         fputs(line,fd);
  195.     }
  196.  
  197.     fclose(fd);
  198.  
  199.     /* trash the updated mail folder if it is empty */
  200.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  201.         unlink(folder_pathname);
  202.  
  203.     fclose(scb->wf);
  204.     scb->wf = NULL;
  205.     xfree((char *)scb->msg_status);
  206.     scb->msg_status = NULL;
  207. }
  208.  
  209. static void near
  210. do_cleanup(scb)
  211. struct pop_scb *scb;
  212. {
  213.     close_folder(scb);
  214.     scb->state = DONE;
  215. }
  216.  
  217. static void near
  218. deletemsg(scb,msg_no)
  219. struct pop_scb *scb;
  220. int msg_no;
  221. {
  222.     unsigned int mask = 1,offset;
  223.  
  224.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  225.         return;
  226.     msg_no--;
  227.     offset = msg_no / BITS_PER_WORD;
  228.     mask <<= msg_no % BITS_PER_WORD;
  229.     scb->msg_status[offset] |= mask;
  230.     scb->folder_modified = TRUE;
  231. }
  232.  
  233. static void near
  234. print_message_length(scb)
  235. struct pop_scb *scb;
  236. {
  237.     char *print_control_string;
  238.  
  239.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  240.         return;
  241.     if (scb->msg_len > 0 || scb->msg_num <= scb->folder_len)
  242.         print_control_string = length_rsp;
  243.     else
  244.         print_control_string = no_more_rsp;
  245.  
  246.     usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  247. }
  248.  
  249. static void near
  250. get_message(scb,msg_no)
  251. struct pop_scb    *scb;
  252. int msg_no;
  253. {
  254.     char line[BUF_LEN], *cp;
  255.  
  256.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  257.         return;
  258.  
  259.     scb->msg_len = 0;
  260.  
  261.     if (msg_no > scb->folder_len) {
  262.         scb->curpos  = 0;
  263.         scb->nextpos = 0;
  264.         return;
  265.     } else {
  266.         /* find the message and its length */
  267.         rewind(scb->wf);
  268.         while (!feof(scb->wf) && (msg_no > -1)) {
  269.             if (msg_no > 0)
  270.                 scb->curpos = ftell(scb->wf);
  271.  
  272.             fgets(line,BUF_LEN,scb->wf);
  273.             if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  274.                 *cp = '\0';
  275.  
  276.             if (isSOM(line))
  277.                 msg_no--;
  278.  
  279.             if (msg_no != 0)
  280.                 continue;
  281.  
  282.             scb->nextpos  = ftell(scb->wf);
  283.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  284.         }
  285.     }
  286.  
  287.     if (scb->msg_len > 0)
  288.         fseek(scb->wf,scb->curpos,SEEK_SET);
  289.  
  290.     /* we need the pointers even if the message was deleted */
  291.     if(isdeleted(scb,scb->msg_num))
  292.         scb->msg_len = 0;
  293. }
  294.  
  295. static void near
  296. read_message(scb)
  297. struct pop_scb    *scb;
  298. {
  299.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  300.         return;
  301.  
  302.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  303.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  304.     else
  305.         scb->msg_num++;
  306.  
  307.     get_message(scb,scb->msg_num);
  308.     print_message_length(scb);
  309.     scb->state  = ITEM;
  310. }
  311.  
  312. static void near
  313. retrieve_message(scb)
  314. struct pop_scb    *scb;
  315. {
  316.     char line[BUF_LEN], *cp;
  317.     long cnt;
  318.  
  319.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  320.         return;
  321.  
  322.     if (scb->msg_len == 0) {
  323.         state_error(scb,"Msg already deleted");
  324.         return;
  325.     }
  326.     cnt  = scb->msg_len;
  327.  
  328.     while(!feof(scb->wf) && (cnt > 0)) {
  329.         fgets(line,BUF_LEN,scb->wf);
  330.         if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  331.             *cp = '\0';
  332.         usprintf(scb->socket,msg_line,line);
  333.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  334.     }
  335.     scb->state = NEXT;
  336. }
  337.  
  338. static void near
  339. open_folder(scb)
  340. struct pop_scb    *scb;
  341. {
  342.     char folder_pathname[64], line[BUF_LEN];
  343.     FILE *fd;
  344.     struct stat folder_stat;
  345.  
  346.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  347.     scb->folder_len = 0;
  348.     scb->folder_file_size = 0;
  349.  
  350.     if (stat(folder_pathname,&folder_stat)){
  351.          usputs(scb->socket,no_mail_rsp);
  352.          return;
  353.     }
  354.     scb->folder_file_size = folder_stat.st_size;
  355.     if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
  356.         return;
  357.  
  358.     if ((scb->wf = temp_file(0,1)) == NULLFILE) {
  359.         fclose(fd);
  360.         return;
  361.     }
  362.     while(!feof(fd)) {
  363.         fgets(line,BUF_LEN,fd);
  364.  
  365.         /* scan for begining of a message */
  366.         if (isSOM(line))
  367.           scb->folder_len++;
  368.  
  369.         /* now put  the line in the work file */
  370.         fputs(line,scb->wf);
  371.  
  372.     }
  373.     fclose(fd);
  374.  
  375.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  376.  
  377.     if ((((scb->folder_len) % BITS_PER_WORD) != 0)
  378.       || (scb->msg_status_size == 0))
  379.         scb->msg_status_size++;
  380.  
  381.     if ((scb->msg_status = (unsigned int *)cxallocw(scb->msg_status_size,
  382.                 sizeof(unsigned int))) == NULL) {
  383.         state_error(scb,"Unable to create message status array");
  384.         return;
  385.     }
  386.  
  387.     usprintf(scb->socket,count_rsp,scb->folder_len);
  388.  
  389.     scb->state  = MBOX;
  390. }
  391.  
  392. #ifdef POP_FOLDERS
  393. static void near
  394. select_folder(scb)
  395. struct pop_scb    *scb;
  396. {
  397.     char *cp = scb->buf;
  398.  
  399.     while(*cp++ != ' ') ;
  400.     strcpy(scb->username,cp);
  401.  
  402.     if (scb->wf != NULL)
  403.         close_folder(scb);
  404.     open_folder(scb);
  405. }
  406. #endif
  407.  
  408. static int near
  409. poplogin(username,pass)
  410. char *pass;
  411. char *username;
  412. {
  413.     char buf[BUF_LEN], *cp, *cp1;
  414.     FILE *fp;
  415.  
  416.     if((fp = fopen(Popusers,READ_TEXT)) == NULLFILE) {
  417.         /* User file doesn't exist */
  418.         return(FALSE);
  419.     }
  420.  
  421.     while(fgets(buf,BUF_LEN,fp),!feof(fp)) {
  422.         if(buf[0] == '#')
  423.             continue;    /* Comment */
  424.  
  425.         if((cp = strchr(buf,':')) == NULLCHAR)
  426.             /* Bogus entry */
  427.             continue;
  428.  
  429.         *cp++ = '\0';        /* Now points to password */
  430.         if(strcmp(username,buf) == 0)
  431.             break;        /* Found user name */
  432.     }
  433.  
  434.     if(feof(fp)) {
  435.         /* User name not found in file */
  436.         fclose(fp);
  437.         return(FALSE);
  438.     }
  439.     fclose(fp);
  440.  
  441.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  442.         return(FALSE);
  443.  
  444.     *cp1 = '\0';
  445.     if(strcmp(cp,pass) != 0) {
  446.         /* Password required, but wrong one given */
  447.  
  448.         return(FALSE);
  449.     }
  450.     /* whew! finally made it!! */
  451.     return(TRUE);
  452. }
  453.  
  454. static void near
  455. pop_sm(scb)
  456. struct pop_scb *scb;
  457. {
  458.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  459.         return;
  460.  
  461.     switch(scb->state) {
  462.     case AUTH:
  463.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)-2) == 0) {
  464.             char password[40], *cp1, *cp = scb->buf;
  465.             while(*cp++ != ' ') ;
  466.             cp1 = cp;
  467.             while(*++cp != ' ') ;
  468.             *cp = '\0';
  469.             strcpy(scb->username,cp1);
  470.             strcpy(password,++cp);
  471.             if (!poplogin(scb->username,password)) {
  472. #ifdef LOG
  473.                 log(scb->socket,"POP2  access DENIED to %s",scb->username);
  474. #endif
  475.                 state_error(scb,"Access denied");
  476.                 return;
  477.             }
  478. #ifdef LOG
  479.             log(scb->socket,"POP2  access granted to %s",scb->username);
  480. #endif
  481.             open_folder(scb);
  482.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  483.             do_cleanup(scb);
  484.         } else
  485.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  486.         break;
  487.  
  488.     case MBOX:
  489.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  490.             read_message(scb);
  491.  
  492. #ifdef POP_FOLDERS
  493.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  494.             select_folder(scb);
  495. #endif
  496.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  497.             do_cleanup(scb);
  498.         } else
  499.             state_error(scb,
  500. #ifdef POP_FOLDERS
  501.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  502. #else
  503.                     "(MBOX) Expected READ or QUIT command");
  504. #endif
  505.         break;
  506.  
  507.     case ITEM:
  508.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  509.             read_message(scb);
  510. #ifdef POP_FOLDERS
  511.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  512.             select_folder(scb);
  513. #endif
  514.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  515.             retrieve_message(scb);
  516.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  517.             do_cleanup(scb);
  518.         else
  519.             state_error(scb,
  520. #ifdef POP_FOLDERS
  521.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  522. #else
  523.                "(ITEM) Expected READ, RETR, or QUIT command");
  524. #endif
  525.         break;
  526.  
  527.     case NEXT:
  528.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  529.             /* ACKD processing */
  530.             deletemsg(scb,scb->msg_num);
  531.             scb->msg_num++;
  532.             get_message(scb,scb->msg_num);
  533.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  534.             /* ACKS processing */
  535.             scb->msg_num++;
  536.             get_message(scb,scb->msg_num);
  537.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  538.             /* NACK processing */
  539.             fseek(scb->wf,scb->curpos,SEEK_SET);
  540.         } else {
  541.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  542.             return;
  543.         }
  544.         print_message_length(scb);
  545.         scb->state  = ITEM;
  546.         break;
  547.  
  548.     case DONE:
  549. /*        do_cleanup(scb);    now done by the pop process */
  550.         break;
  551.  
  552.     default:
  553.         state_error(scb,"(TOP) State Error!!");
  554.         break;
  555.     }
  556. }
  557.  
  558. static void
  559. popserv(s,unused,p)
  560. int s;
  561. void *unused;
  562. void *p;
  563. {
  564.     struct pop_scb *scb;
  565.  
  566.     sockowner(s,Curproc);        /* We own it now */
  567.     log(s,"POP2  open");
  568.     sockmode(s,SOCK_ASCII);
  569.  
  570.     if((scb = (struct pop_scb *)mxallocw(sizeof(struct pop_scb))) == NULLSCB) {
  571.         tputs(Nospace);
  572.         close_s(s);
  573.         return;
  574.     }
  575.     scb->folder_modified = FALSE;
  576.     scb->socket = s;
  577.     scb->state  = AUTH;
  578.  
  579.     usprintf(scb->socket,greeting_msg,Hostname);
  580.  
  581. loop:
  582.     for(;;) {
  583.         if (scb->state == DONE
  584.           || (scb->count = recvline(s,scb->buf,BUF_LEN)) == -1) {
  585.             /* He closed on us */
  586.             break;
  587.         }
  588.         rip(scb->buf);
  589.         if (*scb->buf == '\0')        /* Ignore blank cmd lines */
  590.             goto loop;
  591.         pop_sm(scb);
  592.     }
  593.     do_cleanup(scb);
  594.     if(scb->count != -1)
  595.         usputs(scb->socket,signoff_msg);
  596.     log(scb->socket,"POP2  close");
  597.     close_s(scb->socket);
  598.     if (scb->wf != NULL)
  599.         fclose(scb->wf);
  600.     if (scb->msg_status  != NULL)
  601.         xfree((char *)scb->msg_status);
  602.     xfree((char *)scb);
  603.  
  604. }
  605.  
  606.  
  607.  
  608. /* ---------------------- POP start/stop cmds ---------------------- */
  609.  
  610. /* Start up POP receiver service */
  611. int
  612. pop1(argc,argv,p)
  613. int argc;
  614. char *argv[];
  615. void *p;
  616. {
  617.     struct sockaddr_in lsocket;
  618.     int s;
  619.  
  620.     if (Spop != -1)
  621.         return 0;
  622.  
  623.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  624.     chname(Curproc,"POP2 listener");
  625.  
  626.     lsocket.sin_family = AF_INET;
  627.     lsocket.sin_addr.s_addr = INADDR_ANY;
  628.     lsocket.sin_port = (argc < 2) ? IPPORT_POP2 : atoi(argv[1]);
  629.  
  630.     Spop = socket(AF_INET,SOCK_STREAM,0);
  631.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  632.     listen(Spop,1);
  633.  
  634.     for (;;) {
  635.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  636.             break;    /* Service is shutting down */
  637.  
  638.         if(availmem() < Memthresh){
  639.             usputs(s,Nospace);
  640.             shutdown(s,1);
  641.         } else {
  642.             sockmode(s,SOCK_ASCII);
  643.             /* Spawn a server */
  644.             newproc("POP2 server",1536,popserv,s,NULL,NULL,0);
  645.         }
  646.     }
  647.     return 0;
  648. }
  649.  
  650. /* Shutdown POP service (existing connections are allowed to finish) */
  651. int
  652. pop0(argc,argv,p)
  653. int argc;
  654. char *argv[];
  655. void *p;
  656. {
  657.     close_s(Spop);
  658.     Spop = -1;
  659.     return 0;
  660. }
  661.  
  662. #endif /* POP2_SERVER */